#ifndef ERODE_COND_H_
#define ERODE_COND_H_

#include "../base_filter.h"
#include "erode_cond_detail.h"
#include <boost/gil/gil_all.hpp>

namespace boost {
namespace gil {
namespace extension {
namespace filters {
namespace morph {

template<typename STRUCT_ELEM>
class erode_cond: public boost::gil::extension::filters::base_filter {
public:
    erode_cond(const int threshold);
    virtual ~erode_cond();

    virtual void execute(image_t& image);

//private:
    const int threshold;
    int eroded_cnt;
};

template<typename STRUCT_ELEM>
erode_cond<STRUCT_ELEM>::erode_cond(const int threshold) : threshold(threshold),
        eroded_cnt(0) {
}

template<typename STRUCT_ELEM>
erode_cond<STRUCT_ELEM>::~erode_cond() {
}

template<typename STRUCT_ELEM>
void erode_cond<STRUCT_ELEM>::execute(image_t& src_image) {
    using namespace boost::gil;

    typedef color_element_type<image_t::view_t::value_type, gray_color_t>::type
            gray_level_t;
    typedef image_t::view_t::xy_locator locator_t;

    const STRUCT_ELEM struct_elem;
    const typename STRUCT_ELEM::values_array_t& values = struct_elem.get_values();
    const typename STRUCT_ELEM::origin_t origin = struct_elem.get_origin();

    // structuring element height
    const typename STRUCT_ELEM::values_array_t::size_type se_h = values.shape()[0];
    // structuring element width
    const typename STRUCT_ELEM::values_array_t::size_type se_w = values.shape()[1];

    image_t::view_t src_view = src_image._view;

    detail::size_lookup lookup(src_view);

    image_t dst_image(src_view.width(), src_view.height());
    image_t::view_t dst_view = dst_image._view;

    const int img_h = src_view.height();
    const int img_w = src_view.width();

    locator_t src_loc = src_view.xy_at(0, 0);
    locator_t dst_loc = dst_view.xy_at(0, 0);

    eroded_cnt = 0;

    for (int img_y = 0; img_y < img_h; img_y++) {
        for (int img_x = 0; img_x < img_w; img_x++) {
            if (lookup.getSize(img_x, img_y) > threshold) {
                gray_level_t min = src_loc(0, 0);

                for (unsigned int se_y = 0; se_y < se_h; se_y++) {
                    for (unsigned int se_x = 0; se_x < se_w; se_x++) {
                        gray_level_t curr;

                        int rel_x = se_x - origin.x;
                        int rel_y = se_y - origin.y;

                        int x = img_x + rel_x;
                        int y = img_y + rel_y;

                        if ((x < 0) || (x >= img_w) || (y < 0) || (y >= img_h)
                                || !values[se_y][se_x]) {
                            continue;
                        }

                        curr = src_loc(rel_x, rel_y);
                        if (curr < min) {
                            min = curr;
                        }
                    }
                }

                if (src_loc(0, 0) != min) {
                    eroded_cnt++;
                }

                dst_loc(0, 0) = min;
            } else {
                dst_loc(0, 0) = src_loc(0, 0);
            }

            ++src_loc.x();
            ++dst_loc.x();
        }

        // go to next row of pixels
        src_loc += boost::gil::point2<std::ptrdiff_t> (-src_view.width(), 1);
        dst_loc += boost::gil::point2<std::ptrdiff_t> (-dst_view.width(), 1);
    }

    src_image.swap(dst_image);
}

}
}
}
}
}

#endif /* ERODE_COND_H_ */
